home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Gold Collection / Software Vault - The Gold Collection (American Databankers) (1993).ISO / cdr49 / 109_01.zip / TELNET.C < prev    next >
Text File  |  1993-06-26  |  21KB  |  859 lines

  1.  
  2.  
  3. #define TITLE "BDS Telnet version 2.3        (July 1980)"
  4.  
  5. /*
  6.  
  7.     Written by Leor Zolman and Leo Kenen
  8.     December 1979, March 1980, May 1980, July 1980
  9.  
  10.     This version has been modified to obtain all hardware-
  11.     dependent information from bdscio.h, which must    contain
  12.     the correct hardware specifications for your modem port.
  13.     It is also no longer necessary to alter #define statements
  14.     in this file to reflect CP/M system size; the "topofmem()"
  15.     function is now used to determine the amount of memory 
  16.     available for the text collection buffer.
  17.  
  18.     If you intend to use this program for high speed
  19.     (i.e, greater than 300 baud) data transfers, such as
  20.     maybe over RS232 lines between two machines directly,
  21.     then the speed of transfer will be limited by the
  22.     processors involved instead of the baud rate;
  23.     UNDER SUCH CIRCUMSTANCES, A TRANSFER WILL ONLY WORK IF
  24.     THESE TWO CONDITIONS ARE MET:
  25.  
  26.         1) The transfer must always performd in BINARY
  27.            mode, never in TEXT mode, and
  28.         2) The receiving processor must be as fast or
  29.            FASTER than the transmitting processor. That
  30.            is, a 2 MHz machine may transmit to a 4 MHz
  31.            machine at, say, 9600 baud, BUT NOT VICE-
  32.            VERSA.
  33.  
  34.     See the write up by Leo for more details than are
  35.     presented here.
  36.  
  37.     ******************************************
  38.     * Telnet assumes that your CP/M console  *
  39.     * I/O device is much faster than your     *
  40.     * modem. On a 2MHz 8080, the modem can     *
  41.     * be receiving at up to 300 baud as long *
  42.     * as your console whips along (at the     *
  43.     * very least) at about 1200. 4 MHz mach- *
  44.     * ines might be able to get away with     *
  45.     * slower terminals, but not much slower. *
  46.     ******************************************
  47.  
  48. "Telnet" is a program which interacts with a modem to turn
  49.  your microcomputer into a very versatile terminal. Special
  50.  commands are entered to the program by typing the character
  51.  you designate as "SPECIAL", i.e, some character (such as the
  52.  null or ^A ) which you wouldn't be likely to need transmitted,
  53.  and then entering the appropriate command letter. Incoming
  54.  data may be buffered up in RAM memory and dumped to disk
  55.  whenever you desire (via the "o", "d", "c" and "k" commands),
  56.  data may be transmitted from disk to modem (via "t" and "a"),
  57.  or files can be formally transferred in an alternate
  58.  "checksum" mode which handles handshaking and buffering
  59.  automatically when interacting with the same program on
  60.  the other end of the line. During file transfers, you
  61.  may temporarily pause and later resume the transmission
  62.  (via the "p" and "r" commands.) There are also various
  63.  options you can control (see "n", "7", "h" and "l") to
  64.  adapt operation toward the type of file you wish to
  65.  transfer. The "q" command closes the output file (if open)
  66.  and quits to CP/M. The "s" command displays the status of
  67.  the program. "z" clears the console screen. Any other
  68.  command letter (such as, for example, "?") causes a list
  69.  of legal commands to be displayed.
  70.  
  71. In order to transmit or receive files in the checksum
  72. mode, both parties must make sure that their modems are
  73. operating in FULL-DUPLEX. When you are in full duplex,
  74. then what you type will NOT come right back at you from the
  75. modem; the only input you see from the modem is the data
  76. transmitted by the machine on the OTHER end of the line.
  77.  
  78. This program considers "half duplex" to be any situation
  79. in which the data you transmit comes right back at you;
  80. whether it is your modem that is performing the ehoing
  81. or a computer system far away doesn't really matter. In any
  82. case, checksumming and handshaking is not allowed under half-
  83. duplex operation, since erroneous characters would be received.
  84. When you run telnet, it will ask you whether
  85. or not you are in half-duplex, and perform accordingly. If
  86. you switch from half to full or vice-versa while running the
  87. program, use the "h" option to inform telnet of the fact.
  88.  
  89. To perform checksummed file transfer, a connection must
  90. first be established between the two parties. If both
  91. parties are operating in full duplex, one originating and the
  92. other answering, then telnet will both display what each
  93. types to the console and send it to the modem. If a file
  94. then needs to be transferred, then one user would give the
  95. "t" command (to transmit) and the other would give the "o"
  96. command (to open an output file.) If both users indicate
  97. checksum mode (rather than only one specifing checksum mode
  98. which will abort almost immediatly), then telnet will take
  99. it from there and perform the transfer. If the sender
  100. (transmitter) wants to suspend the transfer temporarily and
  101. continue later, he can use the "p" command. When the receiver
  102. sees that transmission has been suspended (when no data has
  103. been sent for a long time), then HE gives the "p" command also,
  104. and both users may type to each other. When ready to resume,
  105. the "r" command must be given by the RECEIVER first, and
  106. then the sender, to prevent data from being lost.
  107.  
  108. */
  109.  
  110. #include "bdscio.h"    /* System, h'ware constants    */
  111.  
  112. #define SPECIAL 0x1e    /*  The character you type to
  113.                signal a Telnet command
  114.                (should be obscure...I use
  115.                 a "control-shift-uparrow")    */
  116.  
  117.  
  118. /*
  119.     The following #defines need not be changed:
  120. */
  121.  
  122. #define    ACK    0x06    /* Ascii ACK for handshaking    */
  123. #define    NAK    0x15
  124. #define    EOT    0x04    /* End of transmission        */
  125. #define    ETX    0x03    /* Abort Transmission        */
  126.  
  127.  
  128. /*
  129.     External variable declarations:
  130. */
  131.  
  132. char rflag;        /* receiving file open flag    */
  133. char tflag;        /* transmitting file open flag    */
  134. char chflag;        /* checksumming enabled flag    */
  135. char cflag;        /* text-collection enabled flag */
  136. char pflag;        /* pausing flag         */
  137. char spflag;        /* stripping parity bit flag    */
  138. char lflag;        /* list device enabled flag    */
  139. char nflag;        /* recognizing nulls flag    */
  140. char fflag;        /* true if changing CR-LF's into
  141.                 just CR when transmitting    */
  142. char lastc;        /* last char xmitted        */
  143. char dodflag;        /* true if displaying outging
  144.                data                */
  145. char didflag;        /* true if displaying incoming
  146.                data                */
  147. char hdflag;        /* true if effectively working
  148.                in half-duplex        */
  149. char abortf;        /* true when file I/O aborted    */
  150. char rbuf[BUFSIZ];     /* file I/O buffer for incoming
  151.                data file            */
  152. char tbuf[SECSIZ];     /* sector buffer for file being
  153.                transmitted            */
  154. char rname[20];     /* name of receiving file    */
  155. char tname[20];     /* name of transmitting file    */
  156. int rfd, tfd;        /* file descriptors        */
  157.  
  158. char *cptr;        /* pointer to free space in buf */
  159. unsigned free;        /* number of bytes free in buf    */
  160. int bcount;        /* counts bytes in current block
  161.                when checksumming        */
  162. int scount;        /* Number of sectors
  163.                  sent/received        */
  164. int checksum;        /* the checksum value itself    */
  165. char timoutf;        /* true if time-out happens
  166.                while waiting for modem data    */
  167. char *i;        /* odd-job char pointer     */
  168.  
  169. int dod_sav, did_sav;    /* scratch variables        */
  170.  
  171. unsigned bufspace;    /* # of bytes available for text
  172.                collection buffer in ram    */
  173.  
  174. char *buf;        /* text collection pointer; will
  175.                point to the location just
  176.                after itself            */
  177.  
  178. char toupper();        /* This makes for better code
  179.                than if we let it default
  180.                to "int"            */
  181.  
  182. /*
  183.     Routine to return true if input is present on
  184.     the modem:
  185. */
  186.  
  187. miready()
  188. {
  189.     return (inp(MSTAT) & MIMASK) == (MAHI ? MIMASK : 0);
  190. }
  191.  
  192.  
  193. /*
  194.     Routine to return true if modem is ready to output
  195.     a byte:
  196. */
  197.  
  198. moready()
  199. {
  200.     return (inp(MSTAT) & MOMASK) == (MAHI ? MOMASK : 0);
  201. }
  202.  
  203.  
  204. main()
  205. {
  206.     char c, c2;
  207.     int n;
  208.  
  209.     init();
  210.  
  211.   loop:    if (abortf) {
  212.         if (rflag) rclose();
  213.         if (tflag) tabort();
  214.         abortf = 0;
  215.     }
  216.  
  217.     if (tflag && xmit()) {
  218.             printf("\nTransmission complete.\n");
  219.             close(tfd);
  220.             reset();
  221.             }
  222.     if (abortf) goto loop;
  223.     if (miready()) {
  224.       c = c2 = getmod();
  225.       if (spflag) c &= 0x7f;
  226.       if (tflag && (c == ETX)) {
  227.         printf("Reciever has aborted;\n");
  228.         abortf = 1;
  229.         goto loop;
  230.         }
  231.       if (didflag && (c || nflag) && (c != CPMEOF))
  232.             display(c);
  233.       if (cflag && !pflag) {
  234.         if (c || nflag)
  235.           if (!free) printf("**BUFFER FULL**\007\007");
  236.           else { *cptr++ = c; free--; }
  237.         if (chflag) {
  238.         checksum += c2;
  239.         bcount++;
  240.         if (bcount == SECSIZ) {
  241.           bcount = 0;
  242.           outmod(checksum >> 8);
  243.           outmod(checksum);
  244.           checksum = 0;
  245.           c = getmod();
  246.           if (c == EOT) {
  247.             rdump(0); rclose();
  248.             printf("\n%s recieved OK\n",rname);
  249.            }
  250.           else if (c == ACK) {
  251.             if (cptr > buf+1000) rdump(0);
  252.             if (!didflag) printf("Good sector <%d>\n",++scount);
  253.             outmod(0xFD);
  254.            }
  255.           else  {
  256.             cptr -= SECSIZ;
  257.             free += SECSIZ;
  258.             printf("\nChecksum error. Retrying <%d>\n",scount+1);
  259.             outmod(0xFD);
  260.             timoutf = 0;
  261.           }
  262.  
  263.         }
  264.           }
  265.       }
  266.     }
  267.  
  268.     if (kbready()) {
  269.       c = getch();
  270.       if (c != SPECIAL) {
  271.         if (pflag || (!tflag && !(rflag && chflag))) {
  272.         outmod(c);
  273.         if (dodflag) display(c);
  274.           }
  275.        }
  276.       else special();
  277.      }
  278.     goto loop;
  279. }
  280.  
  281.  
  282. /*
  283.     Handle special Telnet command:
  284. */
  285.  
  286. special()
  287. {
  288.         char c;
  289.         int n;
  290.  
  291.         printf("\nSpecial: ");
  292.         if ( (c = getchar()) != '\n') printf("  ");
  293.         switch (toupper(c)) {
  294.         case '\n':  return;
  295.         case SPECIAL: outmod(SPECIAL);
  296.                   printf("Special char sent\n");
  297.                   break;
  298.  
  299.         case '7':  spflag = ask("Strip parity");
  300.                break;
  301.  
  302.         case 'N':  nflag = ask("Recognize incoming nulls");
  303.                break;
  304.  
  305.         case 'F':  fflag = ask("Transmit CR-LF pairs as CR only");
  306.                break;
  307.  
  308.         case 'H':  if (rflag || tflag)  { printf(
  309.                 "Must abort transfer first\n");
  310.                 break;
  311.                 }
  312.                printf("\nAre you either at half");
  313.                printf("-duplex or getting an ");
  314.                hdflag = ask ("echo");
  315.                reset();
  316.                break;
  317.  
  318.         case 'L':  lflag = ask("List incoming data");
  319.                break;
  320.  
  321.         case 'Z':  printf(CLEARS);
  322.                break;
  323.  
  324.         case 'P':  if (pflag) printf("Already pausing");
  325.                else if (!(tflag || rflag))
  326.                   printf("Not transmitting or receiving");
  327.                else {
  328.                 pflag = 1;
  329.                 dod_sav = dodflag;
  330.                 did_sav = didflag;
  331.                 dodflag = !hdflag;
  332.                 didflag = 1;
  333.                 printf("Ok, pausing from %s", tflag ?
  334.                         "transmission" : "collection");
  335.                }
  336.                goto lf;
  337.  
  338.         case 'R':  if (!pflag) printf("Not pausing");
  339.                else {
  340.                 pflag = 0;
  341.                 dodflag = dod_sav;
  342.                 didflag = did_sav;
  343.                 printf("%s now enabled again.", tflag ?
  344.                        "transmission" : "collection");
  345.                }
  346.                goto lf;
  347.  
  348.         case 'K':  printf("Text buffer !ZAPPED!");
  349.                free = bufspace;
  350.                cptr = buf;
  351.                goto lf;
  352.  
  353.         case 'V':  if (rflag) {
  354.                 putchar('\n');
  355.                 i = buf;
  356.                 while (i < cptr) putchar(*i++);
  357.                 printf("\n%u bytes free",free);
  358.                 }
  359.                else printf("No recieving file open");
  360.                goto lf;
  361.  
  362.         case 'O':  if (rflag) rclose();
  363.                if (tflag) tabort();
  364.                printf("\nOutput filename? ");
  365.                gets(rname);
  366.                rflag = 1;
  367.                if (!askstuff()) {
  368.                 rflag = 0;
  369.                 return;
  370.                }
  371.                printf("Creating %s...",rname);
  372.                rfd = fcreat(rname,rbuf);
  373.                if (rfd == ERROR) {
  374.                 printf("Cannot create %s\n",rname);
  375.                 reset();
  376.                 break;
  377.                 }
  378.                putchar('\n');
  379.                cptr = buf;
  380.                free = bufspace;
  381.                rflag = cflag = 1;
  382.                pflag = checksum = bcount = 0;
  383.                if (chflag) {
  384.                 printf("Trying to link...");
  385.                 do {
  386.                   c = getmod();
  387.                   if (abortf) {
  388.                     printf("aborting...\n");
  389.                     unlink(rname);
  390.                     reset();
  391.                     return;
  392.                   }
  393.                   timoutf = 0;
  394.                 } while (c & 0x7f);
  395.                 printf("linked.\n");
  396.                 outmod(0);
  397.                 }
  398.                break;
  399.  
  400.         case 'D':  if (rflag) rdump(1);
  401.                else printf("No output file");
  402.                goto lf;
  403.  
  404.         case 'C':  if (rflag) rclose();
  405.                else printf("No output file");
  406.                goto lf;
  407.  
  408.         case 'Q':  if (tflag) tabort();
  409.                if (rflag) rclose();
  410.                exit();
  411.  
  412.         case 'A':  if (tflag || rflag) {
  413.                        if (chflag) outmod(ETX);
  414.                      abortf = 1;
  415.                           break;
  416.                      }
  417.                printf("No transfer to abort.\n");
  418.                goto lf;
  419.  
  420.         case 'T':  if (tflag) tabort();
  421.                if (rflag) rclose();
  422.                printf("\nFile to transmit? ");
  423.                gets(tname);
  424.                tflag = 1;
  425.                if (!askstuff()) {
  426.                 tflag = 0;
  427.                 return;
  428.                }
  429.                tfd = open(tname,0);
  430.                if (tfd == ERROR) {
  431.                 printf("Cannot open %s\n",tname);
  432.                 reset();
  433.                 goto lf;
  434.                 }
  435.                pflag = checksum = bcount = 0;
  436.                if (read(tfd,tbuf,1) <=0) {
  437.                 printf("Read error from %s\n",
  438.                     tname);
  439.                 abortf = 1;
  440.                 return;
  441.                }
  442.                if (chflag) {
  443.                 printf("Trying to link...");
  444.                 while (1) {
  445.                  outmod(0);
  446.                  for (n=0; n<5000; n++)
  447.                 if (miready()) {
  448.                  if( !(getmod() & 0x7f)) {
  449.                     printf("linked.\n");
  450.                     return;
  451.                   }
  452.                  }
  453.                 else if (kbabort()) {
  454.                     printf("aborting.\n");
  455.                     return;
  456.                      }
  457.                  }
  458.                 }
  459.             break;
  460.  
  461.         case 'S':  dostat();
  462.                goto lf;
  463.  
  464.         default:   prcoms();
  465.  
  466.       lf:       putchar('\n');
  467.     }
  468. }
  469.  
  470. /*
  471.     Print out legal Telnet commands:
  472. */
  473.  
  474. prcoms()
  475. {
  476.     printf("\nBDS Telnet commands are:\n");
  477.     printf("Double SPECIAL: send SPECIAL\n");
  478.     printf("o: Open output file, start collection\n");
  479.     printf("p: Pause (suspend collection or transmission)\n");
  480.     printf("r: Resume after pausing\n");
  481.     printf("d: Dump (append) text buffer to output file\n");
  482.     printf("c: Close output file (after dumping buffer)\n");
  483.     printf("v: View contents of text buffer\n");
  484.     printf("k: Kill (erase) contents of text buffer\n");
  485.     printf("t: Transmit a file to modem\n");
  486.     printf("a: Abort transfer of file\n");
  487.     printf("n: accept or ignore Nulls\n");
  488.     printf("7: select policy regarding Parity bits\n");
  489.     printf("f: select whether to transmit CR-LF as just CR\n");
  490.     printf("h: set Half/full duplex mode\n");
  491.     printf("l: control CP/M List device\n");
  492.     printf("z: clear console terminal screen\n");
  493.     printf("s: display Status of Telnet\n");
  494.     printf("q: dump & close output file (if open) and Quit to CP/M");
  495. }
  496.  
  497.  
  498.  
  499. /*
  500.     Print opening message and initialize program:
  501. */
  502.  
  503. init()
  504. {
  505.     printf(TITLE);
  506.     timoutf = cflag = nflag = lflag = pflag = abortf = fflag = 0;
  507.     spflag = 1;
  508.     lastc = 0;
  509.     buf = &buf + 1;
  510.     bufspace = buf + 500 - topofmem();   /* compute space available */
  511.     bufspace = -bufspace;             /* for text collection buf */
  512.     printf("\n\nAnswer `y' if either your modem is set to half-duplex,\n");
  513.     printf("or you expect an echo from the system on the");
  514.     printf(" other end\n");
  515.     printf("of the line; else answer `n':\n");
  516.     hdflag = ask("Do you expect an echo");
  517.     reset();
  518.     printf("OK; you're on line...\n\n");
  519. }
  520.  
  521.  
  522. /*
  523.     Get all the info pertinent to a file transfer; i.e,
  524.     whether or not the file is text (and needs parity
  525.     stripped, nulls ignored, echoing to console, etc.),
  526.     whether or not checksumming and handshaking are
  527.     required (they always go together), and make sure
  528.     the user is in full duplex mode.
  529. */
  530.  
  531. askstuff()
  532. {
  533.     printf("\n%s ",rflag ? "recieving" : "transmitting");
  534.     if (ask("text (y) or binary data (n)  ")) {
  535.         nflag = 0;
  536.         spflag = didflag = 1;
  537.         dodflag = !hdflag;
  538.         printf("Stripping parity, ignoring nulls,\n");
  539.         printf("  %sdisplaying %s data.\n",
  540.         (rflag ? didflag : dodflag) ? "" : "not ",
  541.             rflag ? "incoming" : "outgoing");
  542.     }    
  543.     else {
  544.         spflag = didflag = dodflag = 0;
  545.         nflag = 1;
  546.         printf("%s all data verbatim, and not\n",
  547.             rflag ? "Recieving" : "Sending");
  548.         printf("displaying it on the console.\n");
  549.      }
  550.  
  551.     putchar('\n');
  552.     printf("Handshaking & checksumming can only happen\n");
  553.     printf("if the other computer has this same program\n");
  554.     printf("running. Do you want handshaking & checksumming");
  555.     chflag = ask("");
  556.     if (chflag && hdflag) {
  557.         printf("Can't do it unless you can eliminate");
  558.         printf(" the echo! Aborting.\n");
  559.         return 0;
  560.     }
  561.     scount = 0;
  562.     return ask("OK...type y to begin, n to abort:");
  563. }
  564.  
  565. /*
  566.     Routine to print out a string and return true
  567.     if the user responds positively
  568. */
  569.  
  570. int ask(s)
  571. char *s;
  572. {
  573.     char c;
  574.     while (1)
  575.     {
  576.         printf("%s ",s);
  577.         printf("(y/n)? ");
  578.         c = toupper(getchar());
  579.         if (c == 'Y')
  580.         { 
  581.             printf("es\n");    
  582.             return 1;
  583.         }
  584.         else if (c == 'N')
  585.         {
  586.             printf("o\n");
  587.             return 0;
  588.         }
  589.         else putchar('\n');
  590.     }
  591. }
  592.  
  593.  
  594. /*
  595.     Print out state of Telnet program:
  596. */
  597.  
  598. dostat()
  599. {
  600.  
  601.        putchar('\n');
  602.  
  603.        if (rflag) {
  604.         printf("Output file = %s\n",rname);
  605.         printf("Text buffer has %u bytes free",
  606.             free);
  607.         printf("\nText collection: ");
  608.           if (cflag) if (pflag) printf("on, but pausing\n");
  609.                  else printf("on\n");
  610.           else printf("off\n");
  611.         }
  612.        else printf("No output file\n");
  613.     
  614.        if (tflag) {
  615.         printf("Transmitting: %s ",
  616.                 tname);
  617.         if (pflag) printf("(but pausing)");
  618.         putchar('\n');
  619.         }
  620.        else printf("Not transmitting any file\n");
  621.     
  622.        printf("Incoming nulls are being %s\n",
  623.          nflag ?"collected" : "ignored");
  624.     
  625.        printf("Parity bits are being %s\n",
  626.          spflag ?"stripped" : "preserved");
  627.     
  628.        printf("Half-duplex mode: %s",
  629.         hdflag ? "on" : "off");
  630. }
  631.  
  632.  
  633. /*
  634.     Routine to dump contents of the memory text buffer
  635.     to the output file and clear the buffer for more
  636.     data:
  637.     (Note that the "else putchar('\0');" clause may not
  638.     be necessary on your system; this is here only to
  639.     make up for a strange "feature" of Lifeboat's 
  640.     Northstar CBIOS where disk polling happens during
  641.     console output, potentially causing bytes to be 
  642.     missed from the modem.)
  643. */
  644.  
  645. rdump(n)
  646. {
  647.     for (i=buf; i<cptr; i++) putc(*i,rbuf);
  648.     cptr = buf;
  649.     free = bufspace;
  650.     if (n) printf("\nBuffer written\n");
  651.     else putchar('\0');
  652. }
  653.  
  654.  
  655. /*
  656.     Routine to dump and close the output file:
  657. */
  658.  
  659. rclose()
  660. {
  661.     rdump(1);
  662.     printf(" Closing %s ",rname);
  663.     if (!chflag) putc(CPMEOF,rbuf);
  664.     fflush(rbuf);
  665.     close(rfd);
  666.     reset();
  667.     putchar('\n');
  668. }
  669.  
  670. /*
  671.     Routine to reset telnet
  672. */
  673.  
  674. reset()
  675. {
  676.     timoutf = rflag = tflag = chflag = cflag = 0;
  677.     scount = 0;
  678.     spflag = 1;
  679.     dodflag = !hdflag;
  680.     didflag = 1;
  681. }
  682.  
  683.  
  684. /*
  685.     Get a byte from the modem:
  686. */
  687.  
  688. getmod()
  689. {
  690.     char c;
  691.     unsigned n;
  692.     if (timoutf) return;
  693.     for (n=20000; !miready() && n; n--)
  694.        if (kbabort()) return;
  695.     if (!n) {
  696.         timoutf = 1;
  697.         return 1;
  698.      }
  699.     c = inp(MDATA);
  700.     if (MRESET) outp(MSTAT,MRESETVAL);
  701.     return c;
  702. }
  703.  
  704.  
  705. /*
  706.     Output a byte to the modem:
  707. */
  708.  
  709.  
  710. outmod(c)
  711. char c;
  712. {
  713.     while (!moready())
  714.         if (kbabort()) return;
  715.     outp(MDATA,c);
  716. }
  717.  
  718. kbready()
  719. {
  720.     return bios(2);
  721. }
  722.  
  723.  
  724.  
  725. /*
  726.     Get a character from the keyboard:
  727.     (Uses a direct BIOS instead of going through
  728.     the BDOS. By naming this the same as the library
  729.     version of "getchar", we insure that THIS version
  730.     is used by things like "gets" instead of the library
  731.     version.)
  732. */
  733.  
  734. getchar()
  735. {
  736.     char c;
  737.     c = getch();
  738.     if (c == '\r') c = '\n';
  739.     putchar(c);
  740.     return c;
  741. }
  742.  
  743.  
  744. getch()
  745. {
  746.     return bios(3);
  747. }
  748.  
  749.  
  750.  
  751. /*
  752.     Return true if keyboard hit and SPECIAL
  753.     typed:
  754. */
  755.  
  756. kbabort()
  757. {
  758.     if (kbready() && getch() == SPECIAL) {
  759.         abortf = 1;
  760.         return 1;
  761.     }
  762.     return 0;
  763. }
  764.  
  765.  
  766. /*
  767.     Write a character to the console.
  768. */
  769.  
  770. putchar(c)
  771. char c;
  772. {
  773.     if (c == '\n') putch2('\r');
  774.     putch2(c);
  775. }
  776.  
  777. putch2(c)
  778. char c;
  779. {
  780.     bios(4,c);
  781. }
  782.  
  783.  
  784. /*
  785.     Write character to console display, and also to
  786.     system list device if that is enabled:
  787. */
  788.  
  789. display(c)
  790. char c;
  791. {
  792.     if (c==CPMEOF) return;
  793.     putch2(c);
  794.     if (lflag) bdos(5,c);
  795. }
  796.  
  797.  
  798. xmit()
  799. {
  800.     int incheck;
  801.     int n;
  802.     char c;
  803.     if (pflag || !moready()) return 0;
  804.     c = tbuf[bcount++];
  805.     checksum += c;
  806.     if ((!(spflag && (c&0x7f)==CPMEOF && !chflag)) &&
  807.         (!(!chflag && c=='\n' && lastc=='\r' &&
  808.           !nflag && fflag))) outmod(c);
  809.     lastc = c;
  810.     if (dodflag) display(c);
  811.     if (bcount != SECSIZ) return 0;
  812.     bcount = 0;
  813.     if (!chflag) return !read1();
  814.     incheck = (getmod() << 8) + getmod();
  815.     if (incheck != checksum) {
  816.         for (n=0; n<20000; n++);     /* let line settle down */
  817.         printf("\nError. Resending sector %d...\n",scount+1);
  818.         outmod(NAK);
  819.     }
  820.     else if (read1()) {
  821.         if (!dodflag) printf("Good sector <%d>\n",++scount);
  822.         outmod(ACK);
  823.     }
  824.           else { outmod(EOT); return 1; }
  825.  
  826.     checksum = 0;
  827.     if (getmod() != 0xFD) {
  828.             printf("\nPhase error; aborting...");
  829.             abortf = 1;
  830.             }
  831.     return 0;
  832. }
  833.  
  834.  
  835. /*
  836.     Read a sector of the transmission file:
  837. */
  838.  
  839. read1()
  840. {
  841.     int i;
  842.     i = read(tfd, tbuf, 1);
  843.     if ( i == ERROR) {
  844.         printf("\nRead error from %s; Aborting.\n",
  845.             tname);
  846.         tabort();
  847.      }
  848.     return i;
  849. }
  850.  
  851. tabort()
  852. {
  853.     if (chflag)  while (bcount++ != 133) outmod(ETX);
  854.     printf("\nTransmission of %s aborted.\n",tname);
  855.      close(tfd);
  856.     reset();
  857. }
  858.  
  859.